// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package androidx.gdata;

import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import java.net.URL;
import java.net.HttpURLConnection;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;

import androidx.xml.XMLUtil;

public abstract class GDataServer {
  private String baseUrl;
  private String username;
  private String auth;

  protected GDataServer(String baseUrl, String username, String auth) {
    this.baseUrl = baseUrl;
    if (username.indexOf('@') == -1) {
      this.username = username + "@gmail.com";
    } else {
      this.username = username;
    }
    this.auth = auth;
  }


  protected Node extractFeedNode(String result) throws IOException {
    return extractNode(result, "feed");
  }

  protected Node extractEntryNode(String result) throws IOException {
    return extractNode(result, "entry");
  }

  protected Node extractNode(String result, String nodeName) throws IOException {
    Document xml;
    try {
      xml = XMLUtil.stringToDocument(result);
    } catch (SAXException e) {
      throw new IOException(e.getMessage());
    }
    Node feedNode = XMLUtil.findNamedElementNode(xml, nodeName);
    if (feedNode == null) {
      throw new IOException("Unrecognized response: no <"+ nodeName +"> element found");
    }
    return feedNode;
  }

  protected String doAuthorizedGet(URL url) throws IOException {
    return doGet(auth, url);
  }

  protected String getBaseUrl() {
    return baseUrl;
  }

  protected String getUsername() {
    return username;
  }

  private static String doGet(String auth, URL url) throws IOException {
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setRequestProperty("Authorization", "GoogleLogin auth=" + auth);
    BufferedReader reader = null;
    try {
      int rc = conn.getResponseCode();
      if (rc != 200) {
        throw new IOException("code " + rc + " '" + conn.getResponseMessage() + "'");
      }
      reader = new BufferedReader(new InputStreamReader(conn.getInputStream()), 512);
      return toString(reader);
    } finally {
      if (reader != null) {
        reader.close();
      }
    }
  }

  protected String doAtomPost(URL url, String input) throws IOException {
    byte[] bytes = input.getBytes();
    return doAtomPost(url, new ByteArrayInputStream(bytes), bytes.length);
  }

  protected String doAtomPut(URL url, String input) throws IOException {
    byte[] bytes = input.getBytes();
    return doAtomPut(url, new ByteArrayInputStream(bytes), bytes.length);
  }

  protected String doAtomDelete(URL url, String input) throws IOException {
    byte[] bytes = input.getBytes();
    return doAtomDelete(url, new ByteArrayInputStream(bytes), bytes.length);
  }

  protected String doAtomPost(URL url, InputStream stuffToPost, int contentLength) throws IOException {
    return doAtomPost(url, stuffToPost, contentLength, null, 201);
  }

  protected String doAtomDelete(URL url, InputStream stuffToPost, int contentLength) throws IOException {
    return doAtomPost(url, stuffToPost, contentLength, "DELETE", 200);
  }


  protected String doAtomPut(URL url, InputStream stuffToPost, int contentLength) throws IOException {
    return doAtomPost(url, stuffToPost, contentLength, "PUT", 200);
  }

  protected String doAtomPost(URL url, InputStream stuffToPost, int contentLength
      , String httpMethod, int expectedResponseCode) throws IOException {
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("POST");
    if (httpMethod != null) {
      conn.setRequestProperty("X-HTTP-Method-Override", httpMethod);
    }
    conn.setRequestProperty("Authorization", "GoogleLogin auth=" + auth);
    conn.setRequestProperty("Content-Type", "application/atom+xml");
    conn.setDoOutput(true);
    if (contentLength > 0) {
      conn.setRequestProperty("Content-Length", Integer.toString(contentLength));
    }
    OutputStream ostr = null;
    try {
      ostr = conn.getOutputStream();
      copy(stuffToPost, ostr);
    } finally {
      if (ostr != null) {
        ostr.close();
      }
    }

    conn.connect();

    BufferedReader reader = null;
    try {
      int rc = conn.getResponseCode();
      if (rc != expectedResponseCode) {
        throw new IOException("code " + rc + " '" + conn.getResponseMessage() + "'");
      }
      reader = new BufferedReader(new InputStreamReader(conn.getInputStream()), 512);
      return toString(reader);
    } finally {
      if (reader != null) {
        reader.close();
      }
    }
  }


  private static void copy(InputStream in, OutputStream out) throws IOException {
    byte[] buf = new byte[512];
    int bytesRead = 1;
    while (bytesRead > 0) {
      bytesRead = in.read(buf);
      if (bytesRead > 0) {
        out.write(buf, 0, bytesRead);
      }
    }
  }

  private static String toString(BufferedReader reader) throws IOException {
    StringBuilder sb = new StringBuilder();
    String line;
    while ( (line = reader.readLine()) != null) {
      sb.append(line).append('\n');
    }
    return sb.toString();
  }
  
}
